package service

import (
	"context"
	"database/sql"
	"encoding/json"
	"fx_swift_boss/api"
	"fx_swift_boss/internal/model"
	"fx_swift_boss/pkg/code"
	"fx_swift_boss/pkg/crypt"
	"fx_swift_boss/pkg/global"
	"fx_swift_boss/pkg/internalerrors"
	"fx_swift_boss/pkg/utils"
	"github.com/gogf/gf/v2/container/gmap"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/spf13/cast"
	"strings"
)

type IAuthSrv interface {
	// 新增/修改 子帐号并且加入权限
	AddBoss(ctx context.Context, req *api.AddBossReq) error

	// 子帐号列表
	GetAccountList(ctx context.Context) ([]*api.GetAccountListRes, error)

	// 新增权限
	AddAuth(ctx context.Context, req *api.AddAuthReq) error

	// 新增/更新/删除 角色对应的权限
	AddRoleAuth(ctx context.Context, req *api.AddRoleAuthReq) error

	// 获取用户对应的权限和角色对应的权限
	GetUserAuth(ctx context.Context) ([]api.AddAuthRes, error)

	// 获取用户对应的权限/角色对应的权限
	GetAuthToUserOrRole(ctx context.Context, req *api.GetAuthToUserOrRoleReq) (*api.GetAuthToUserOrRoleRes, error)

	// 获取所有角色
	GetRoleList(ctx context.Context) ([]*api.GetRoleList, error)
}
type authSrv struct {
}

func NewAuthSrv() IAuthSrv {
	return &authSrv{}
}

// 新增/修改 子帐号并且加入权限
func (a *authSrv) AddBoss(ctx context.Context, req *api.AddBossReq) error {
	var (
		err            error
		hashedPassword string
		salt           string
		num            int
		account        string
	)
	if req.Method == "add" {
		// 查询邮箱是否被注册
		if num, err = global.GFDB.Ctx(ctx).Model("fx_boss").Where("email", req.Email).Count(); err != nil || num == 1 {
			return internalerrors.New(code.ErrUnknown, "邮箱已经被注册了")
		}

		if hashedPassword, salt, err = crypt.HashPassword(req.Password); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		account = utils.GetHashCode(utils.GenerateCode32())

		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if _, err = tx.Insert("fx_boss", &model.FxBoss{
				Account:  account,
				Password: hashedPassword,
				Salt:     salt,
				Email:    req.Email,
				Type:     1,
			}); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			// 加入用户对应的权限
			if len(req.Auths) > 0 {
				for _, obj := range req.Auths {
					if _, err = tx.Insert("fx_user_auth_mapping", &model.FxUserAuthMapping{
						Account:  account,
						AuthName: obj.Name,
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			}
			// 加入用户对应的角色
			if len(req.RoleIds) > 0 {
				for _, roleId := range req.RoleIds {
					if _, err = tx.Insert("fx_user_role_mapping", &model.FxUserRoleMapping{
						Account: account,
						RoleId:  cast.ToInt64(roleId),
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			}
			return nil
		})
	} else if req.Method == "update" {
		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if len(req.Password) > 0 {
				// update password
				if hashedPassword, salt, err = crypt.HashPassword(req.Password); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
				_, err = tx.Ctx(ctx).Model("fx_boss").Data(g.Map{"password": hashedPassword, "salt": salt}).
					Where("account=?", req.Account).Update()
			}
			// 更新用户对应的角色
			if len(req.RoleIds) > 0 {
				if _, err = tx.Model("fx_user_role_mapping").
					Where("account=?", req.Account).Delete(); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
				for _, roleId := range req.RoleIds {
					if _, err = tx.Insert("fx_user_role_mapping", &model.FxUserRoleMapping{
						Account: req.Account,
						RoleId:  cast.ToInt64(roleId),
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			} else {
				if _, err = tx.Model("fx_user_role_mapping").
					Where("account=?", req.Account).Delete(); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
			}
			// 更新用户对应的权限
			if len(req.Auths) > 0 {
				if _, err = tx.Model("fx_user_auth_mapping").
					Where("account=?", req.Account).Delete(); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
				for _, obj := range req.Auths {
					if _, err = tx.Insert("fx_user_auth_mapping", &model.FxUserAuthMapping{
						Account:  req.Account,
						AuthName: obj.Name,
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			} else {
				if _, err = tx.Model("fx_user_auth_mapping").
					Where("account=?", req.Account).Delete(); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
			}
			return nil
		})
	} else if req.Method == "del" {
		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if _, err = global.GFDB.Ctx(ctx).Model("fx_boss").
				Where("account=?", req.Account).Delete(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			if _, err = tx.Model("fx_user_role_mapping").
				Where("account=?", req.Account).Delete(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			if _, err = tx.Model("fx_user_auth_mapping").
				Where("account=?", req.Account).Delete(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			return nil
		})
	}
	return nil
}

// 子帐号列表
func (a *authSrv) GetAccountList(ctx context.Context) ([]*api.GetAccountListRes, error) {
	type DbVo struct {
		RoleName string `json:"role_name"`
		RoleId   string `json:"role_id"`
		Email    string `json:"email"`
		Account  string `json:"account"`
	}
	var (
		err          error
		dbList       = make([]*DbVo, 0)
		result       = make([]*api.GetAccountListRes, 0)
		maps         = gmap.ListMap{}
		roleInfoList []api.RoleInfo
	)

	sql := "SELECT fb.account,fb.email, fr.name AS role_name,fr.id AS role_id FROM `fx_boss` fb " +
		"LEFT JOIN `fx_user_role_mapping` fur ON (fb.account = fur.account and fur.deleted_at is NULL) " +
		"LEFT JOIN `fx_role` fr ON (fur.role_id = fr.id and fr.deleted_at IS NULL) WHERE (fb.type = 1 and fb.deleted_at is NULL)"
	if err = global.GFDB.Ctx(ctx).Raw(sql).Scan(&dbList); err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if len(dbList) > 0 {
		for _, obj := range dbList {
			key := obj.Account + "-" + obj.Email
			if !maps.Contains(key) {
				roleInfoList = make([]api.RoleInfo, 0)
				if len(obj.RoleName) > 0 {
					maps.Set(key, append(roleInfoList, api.RoleInfo{
						Id:   obj.RoleId,
						Name: obj.RoleName,
					}))
				} else {
					maps.Set(key, []api.RoleInfo{})
				}
			} else {
				list := maps.Get(key).([]api.RoleInfo)
				maps.Set(key, append(list, api.RoleInfo{
					Id:   obj.RoleId,
					Name: obj.RoleName,
				}))
			}
		}

		maps.Iterator(func(key, value interface{}) bool {
			result = append(result, &api.GetAccountListRes{
				Account:  strings.Split(key.(string), "-")[0],
				Email:    strings.Split(key.(string), "-")[1],
				RoleInfo: value.([]api.RoleInfo),
			})
			return true
		})
	}
	return result, err
}

// 新增权限
func (a *authSrv) AddAuth(ctx context.Context, req *api.AddAuthReq) error {
	var (
		err error
	)
	if len(req.JsonStr) > 0 {
		list := []api.AddAuth{}
		json.Unmarshal([]byte(req.JsonStr), &list)
		db := g.DB()
		// 执行物理删除
		_, err = db.Delete(ctx, "fx_auth", "1=1")
		err = recursionObj(ctx, list)
		if _, err = g.Redis().Set(ctx, "addAuth", req.JsonStr); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
	}
	return err
}

func recursionObj(ctx context.Context, objs []api.AddAuth) error {
	var (
		err error
	)
	for _, obj := range objs {
		err = global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if _, err = tx.Replace("fx_auth", &model.FxAuth{
				Name: obj.Name,
			}); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			return nil
		})
		err = recursionObj(ctx, obj.Children)
	}
	return err
}

// 新增/更新/删除 角色对应的权限
func (a *authSrv) AddRoleAuth(ctx context.Context, req *api.AddRoleAuthReq) error {
	var (
		err       error
		roleId    int64
		sqlResult sql.Result
	)
	if req.Method == "add" {
		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if sqlResult, err = tx.Insert("fx_role", &model.FxRole{
				Name: req.Name,
			}); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			roleId, _ = sqlResult.LastInsertId()
			if len(req.Auths) > 0 {
				for _, obj := range req.Auths {
					if _, err = tx.Insert("fx_role_auth_mapping", &model.FxRoleAuthMapping{
						RoleId:   roleId,
						AuthName: obj.Name,
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			}
			return nil
		})

	} else if req.Method == "update" {
		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if _, err = tx.Model("fx_role").Data(g.Map{"name": req.Name}).Where("id=?", req.RoleId).
				Update(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			if len(req.Auths) > 0 {
				if _, err = tx.Model("fx_role_auth_mapping").
					Where("role_id=?", req.RoleId).Delete(); err != nil {
					return internalerrors.New(code.ErrUnknown, err.Error())
				}
				for _, obj := range req.Auths {
					if _, err = tx.Insert("fx_role_auth_mapping", &model.FxRoleAuthMapping{
						RoleId:   cast.ToInt64(req.RoleId),
						AuthName: obj.Name,
					}); err != nil {
						return internalerrors.New(code.ErrUnknown, err.Error())
					}
				}
			}
			return nil
		})
	} else if req.Method == "del" {
		return global.GFDB.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if _, err = tx.Model("fx_role").Where("id=?", req.RoleId).
				Delete(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			if _, err = tx.Model("fx_role_auth_mapping").
				Where("role_id=?", req.RoleId).Delete(); err != nil {
				return internalerrors.New(code.ErrUnknown, err.Error())
			}
			return nil
		})
	}
	return nil
}

// 获取用户对应的权限和角色对应的权限
func (a *authSrv) GetUserAuth(ctx context.Context) ([]api.AddAuthRes, error) {
	var (
		err     error
		account string
		result  = make([]api.AddAuthRes, 0)
	)
	account = ctx.Value("Account").(string)
	userToAuthList := make([]api.AddAuthRes, 0)
	roleToAuthList := make([]api.AddAuthRes, 0)
	if account == "admin" {
		if err = global.GFDB.Model("fx_auth fa").
			Fields("fa.name").Scan(&userToAuthList); err != nil {
			return nil, internalerrors.New(code.ErrUnknown, err.Error())
		}
	} else {
		// 先查询用户对应的权限
		if err = global.GFDB.Model("fx_auth fa").
			LeftJoin("fx_user_auth_mapping fuam", "fuam.auth_name=fa.name").
			Fields("fa.name").
			Where("fuam.account=?", account).Scan(&userToAuthList); err != nil {
			return nil, internalerrors.New(code.ErrUnknown, err.Error())
		}
		// 再查询用户对应角色所拥有的权限
		sql := "SELECT f3.name FROM fx_user_role_mapping f1 LEFT JOIN fx_role_auth_mapping f2 ON f1.role_id=f2.role_id " +
			"LEFT JOIN fx_auth f3 ON f2.auth_name = f3.`name` where f1.account=?"
		if err = global.GFDB.Raw(sql, account).Scan(&roleToAuthList); err != nil {
			return nil, internalerrors.New(code.ErrUnknown, err.Error())
		}
	}

	if len(userToAuthList) > 0 && len(roleToAuthList) > 0 {
		unionSlice := make([]api.AddAuthRes, 0)
		uniqueElements := make(map[api.AddAuthRes]bool)

		for _, item := range userToAuthList {
			unionSlice = append(unionSlice, item)
			uniqueElements[item] = true
		}

		for _, items := range roleToAuthList {
			if _, ok := uniqueElements[items]; !ok {
				unionSlice = append(unionSlice, items)
				uniqueElements[items] = true
			}
		}
		result = append(result, unionSlice...)
	} else if len(userToAuthList) > 0 {
		result = append(result, userToAuthList...)
	} else if len(roleToAuthList) > 0 {
		result = append(result, roleToAuthList...)
	}
	return result, nil
}

// 获取用户对应的权限/角色对应的权限
func (a *authSrv) GetAuthToUserOrRole(ctx context.Context, req *api.GetAuthToUserOrRoleReq) (*api.GetAuthToUserOrRoleRes, error) {
	type DbVo struct {
		Name string `json:"name"`
	}
	var (
		err            error
		redisResult    *gvar.Var
		result         = &api.GetAuthToUserOrRoleRes{}
		roleAuthList   = make([]DbVo, 0)
		userToAuthList = make([]DbVo, 0)
		authInfoList   = make([]api.AuthInfo, 0)
	)
	// 获取全部基础权限
	if len(req.RoleName) == 0 && len(req.Account) == 0 {
		redisResult, err = g.Redis().Get(ctx, "addAuth")
		if err != nil {
			return nil, internalerrors.New(code.ErrUnknown, err.Error())
		}
		if redisResult.Val() == nil {
			return result, err
		} else {
			result.JsonStr = redisResult.String()
		}

	} else if len(req.RoleId) > 0 {
		if err = global.GFDB.Model("fx_role_auth_mapping f").
			LeftJoin("fx_auth fa", "f.auth_name = fa.name").
			Fields("fa.name").
			Where("role_id=?", req.RoleId).Scan(&roleAuthList); err != nil {
			return nil, internalerrors.New(code.ErrUnknown, err.Error())
		}
		if len(roleAuthList) > 0 {
			for _, obj := range roleAuthList {
				authInfoList = append(authInfoList, api.AuthInfo{
					Name: obj.Name,
				})
			}
			result.AuthInfo = authInfoList
		}
	} else if len(req.Account) > 0 {
		if req.Account == "admin" {
			if err = global.GFDB.Model("fx_auth fa").
				Fields("fa.name").
				Scan(&roleAuthList); err != nil {
				return nil, internalerrors.New(code.ErrUnknown, err.Error())
			}

			for _, obj := range roleAuthList {
				authInfoList = append(authInfoList, api.AuthInfo{
					Name: obj.Name,
				})
			}
			result.AuthInfo = authInfoList

		} else {
			// 先查询用户对应的权限
			if err = global.GFDB.Model("fx_auth fa").
				LeftJoin("fx_user_auth_mapping fuam", "fuam.auth_name=fa.name").
				Fields("fa.name").
				Where("fuam.account=?", req.Account).Scan(&userToAuthList); err != nil {
				return nil, internalerrors.New(code.ErrUnknown, err.Error())
			}
			// 再查询用户对应角色所拥有的权限
			sql := "SELECT f3.name FROM fx_user_role_mapping f1 LEFT JOIN fx_role_auth_mapping f2 ON f1.role_id=f2.role_id " +
				"LEFT JOIN fx_auth f3 ON f2.auth_name = f3.`name` where f1.account=?"
			if err = global.GFDB.Raw(sql, req.Account).Scan(&roleAuthList); err != nil {
				return nil, internalerrors.New(code.ErrUnknown, err.Error())
			}

			unionSlice := make([]DbVo, 0)
			uniqueElements := make(map[DbVo]bool)

			for _, item := range userToAuthList {
				unionSlice = append(unionSlice, item)
				uniqueElements[item] = true
			}

			for _, items := range roleAuthList {
				if _, ok := uniqueElements[items]; !ok {
					unionSlice = append(unionSlice, items)
					uniqueElements[items] = true
				}
			}

			for _, obj := range unionSlice {
				authInfoList = append(authInfoList, api.AuthInfo{
					Name: obj.Name,
				})
			}
			result.AuthInfo = authInfoList

		}
	}
	return result, err
}

// 获取所有角色
func (a *authSrv) GetRoleList(ctx context.Context) ([]*api.GetRoleList, error) {
	type DbVo struct {
		Id       string `json:"id"`
		Name     string `json:"name"`
		AuthName string `json:"auth_name"`
	}
	var (
		err      error
		result   = make([]*api.GetRoleList, 0)
		dbVoList = make([]DbVo, 0)
		authList []api.AuthInfo
	)
	sql := "SELECT fr.name,fr.id,fa.name AS auth_name FROM `fx_role` fr " +
		"LEFT JOIN `fx_role_auth_mapping` fra ON (fr.id = fra.role_id and `fra`.`deleted_at` IS NULL) " +
		"LEFT JOIN `fx_auth` fa ON (fa.name = fra.auth_name and `fa`.`deleted_at` IS NULL) WHERE `fr`.`deleted_at` IS NULL"
	if err = global.GFDB.Raw(sql).Scan(&dbVoList); err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if len(dbVoList) > 0 {
		maps := gmap.ListMap{}
		for _, obj := range dbVoList {
			Key := obj.Id + "-" + obj.Name
			if !maps.Contains(Key) {
				authList = make([]api.AuthInfo, 0)
				maps.Set(Key, append(authList, api.AuthInfo{
					Name: obj.AuthName,
				}))
			} else {
				lists := maps.Get(Key).([]api.AuthInfo)
				maps.Set(Key, append(lists, api.AuthInfo{
					Name: obj.AuthName,
				}))
			}
		}
		maps.Iterator(func(key, value interface{}) bool {
			keys := key.(string)
			result = append(result, &api.GetRoleList{
				Id:    strings.Split(keys, "-")[0],
				Name:  strings.Split(keys, "-")[1],
				Auths: value.([]api.AuthInfo),
			})
			return true
		})
	}
	return result, err
}
